/**
 * @file BaseSolver.h
 * @author shikang999@126.com
 * @brief 
 * @version 0.1
 * @date 2025-06-18
 * 
 * @copyright Copyright (c) 2025
 * 
 */
#pragma once
#include <list>
#include <vector>
#include <random>
#include <memory>
#include <chrono>
#include <cstdint>
#include "BaseFun.h"

namespace Solver
{
//1.求解器类型
enum class SolverType_E : uint16_t
{
    Nothing_E = 0,
    LG_E      = 1,
    SWO_E     = 2,
    Dogleg_E  = 200,
};
using vecSolverType_t = std::vector<SolverType_E>;

//2.
struct ResetParam_t
{
    ResetParam_t():upY(std::numeric_limits<value_t>::max()), limitR(0.001), iterNum(0), limitIterNum(368)
    {

    }

    value_t upY;
    value_t limitR;
    uint32_t iterNum;
    uint32_t limitIterNum;
};

//3.
class BaseSolver
{
public:
    BaseSolver(SolverType_E type, const std::string &name = "");
    virtual ~BaseSolver();

    //设置ID
    void setID(uint32_t id);

    //获取ID
    uint32_t getID() const;

    //获取求解耗时(s)
    uint32_t getCostTime() const;

    //获取求解器类型
    SolverType_E getType() const;

    //设置间隔几个迭代打印一次结果,如果num为0,则不打印
    void setPrintNum(uint32_t num);

    //设置迭代陷入局部最优时,多少次(num),变化率低于r时,将重新随机初始化值
    void setResetParam(uint32_t num, value_t r);

    //获取求解器名称
    const std::string &getName() const;

    //设置求解结果保存文件路径
    void setSaveResultFile(const std::string &f);

    //设置最小化目标函数
    void setFun(const std::shared_ptr<BaseFun> &fun);

    //设置求解变量范围[a,b]
    bool setVarRange(const VectorXd_t &a, const VectorXd_t &b);

    //设置求解变量范围[a,b],dim为变量维度
    bool setVarRange(value_t a, value_t b, uint32_t dim);

    //停止求解
    void stop();

    //求解, popNum表示种群数量
    bool solve(uint32_t popNum = 90);

    //继续求解
    bool continueSolve();

    //获取条件
    Condition_t &getCondition();

    //等待结束
    void waitExit() const;

    //获取历史迭代Y值
    const vecValue_t &getHistory() const;

    //获取最佳结果. 此函数不保证线程安全,最好在waitExit()函数后调用
    std::shared_ptr<DataValue_t> getResult() const;
    
    //等待所有求解器退出. 多个求解器同时求解时, 使用这个函数等待退出
    static void waitAllSolverExit(const std::vector<std::shared_ptr<BaseSolver>> &solvers);

    //等待线程退出,当前listSolver.size() <= num 时返回函数
    static void waitSolverExit(std::list<std::shared_ptr<BaseSolver>> &listSolver, std::size_t num);

protected:
    //如果有什么参数需要初始化,可以重载这个函数
    virtual bool init();

    //检查是否需要重置初值,需要时直接重置.重置返回True
    bool tryToResetInitValue();

    //求解调用
    bool continueSolveCall(bool continueFlag);

    //一个迭代步求解
    virtual void solveByIter() = 0;

    //求解线程调用函数
    void run();

    //种群初始化
    bool initPop(uint32_t popNum);

    //重置随机值
    void resetValue(std::vector<std::shared_ptr<Variable_t>>::iterator si, std::vector<std::shared_ptr<Variable_t>>::iterator ei);

    //增加迭代次
    void addInter();

    //保存结果
    void saveResult();

    //局部搜索
    bool searchLocal(std::shared_ptr<Variable_t> &var);

    //对var进行局部搜索,如果求解满足停止条件返回True,否则返回False
    bool searchForBestLocal(std::shared_ptr<Variable_t> &var, const value_t startWeight = 0.01);

    //通过梯度进行搜索
    bool searchByDiff(std::shared_ptr<Variable_t>& var, bool forceUpdate = false);

    //计算梯度
    void calcDiff(const VectorXd_t &x, VectorXd_t &diff);

    //3个变量混合,其中一个是最优变量,如果求解满足停止条件返回True,否则返回False
    bool mixBest3(bool updateForce = false);

    //4个变量混合,其中一个是最优变量,如果求解满足停止条件返回True,否则返回False
    bool mixBest4(bool updateForce = false);

    //索引乱序
    void shuffleVarIndex();

    //计算levy飞行
    value_t calcLevy();

    //获取有 num 次没有更新的变量到 outVar
    void getNoUpdateVariable(std::vector<std::shared_ptr<Variable_t>> &outVar, uint32_t num = 168) const;

    //按目标值对对变量进行从小到大排序,执行后 vecVariable_ 将排序
    void sortBestForVariable();

    //变量更新, updateForce表示是否强制更新
    bool updateVariable(std::shared_ptr<Variable_t> &var, bool updateForce = true) const;

    //2变量交叉: x = r1 * x1 + r2 * x2
    void add2(  VectorXd_t &x, 
                value_t r1, const VectorXd_t &x1, 
                value_t r2, const VectorXd_t &x2) const;

    //3变量交叉: x = r1 * x1 + r2 * x2 + r3 * x3
    void add3(  VectorXd_t &x,
                value_t r1, const VectorXd_t &x1, 
                value_t r2, const VectorXd_t &x2, 
                value_t r3, const VectorXd_t &x3) const;

    //4变量交叉: x = r1 * x1 + r2 * x2 + r3 * x3 + r4 * x4
    void add4(  VectorXd_t &x,
                value_t r1, const VectorXd_t &x1, 
                value_t r2, const VectorXd_t &x2, 
                value_t r3, const VectorXd_t &x3,
                value_t r4, const VectorXd_t &x4) const;

    //3变量交叉: x = x1 + r * (x3 - x2)
    void add3Same(  VectorXd_t &x,
                    value_t r,
                    const VectorXd_t &x1, 
                    const VectorXd_t &x2, 
                    const VectorXd_t &x3) const;

    //5变量交叉: x = x1 + r1 * (x3 - x2) + r2 * (x5 - x4)
    void add5Same(  VectorXd_t &x,
                    value_t r1,
                    value_t r2,
                    const VectorXd_t &x1, 
                    const VectorXd_t &x2, 
                    const VectorXd_t &x3,
                    const VectorXd_t &x4, 
                    const VectorXd_t &x5) const;

    //7变量交叉: x = x1 + r1 * (x3 - x2) + r2 * (x5 - x4) + r3 * (x7 - x6)
    void add7Same(  VectorXd_t &x,
                    value_t r1,
                    value_t r2,
                    value_t r3,
                    const VectorXd_t &x1, 
                    const VectorXd_t &x2, 
                    const VectorXd_t &x3,
                    const VectorXd_t &x4, 
                    const VectorXd_t &x5,
                    const VectorXd_t &x6, 
                    const VectorXd_t &x7) const;

    //3变量交叉: x = x1 + r * rand() * (x3 - x2)
    void add3Rand(  VectorXd_t &x,
                    value_t r,
                    const VectorXd_t &x1, 
                    const VectorXd_t &x2, 
                    const VectorXd_t &x3);

    //5变量交叉: x = x1 + r1 * rand() * (x3 - x2) + r2 * rand() * (x5 - x4)
    void add5Rand(  VectorXd_t &x,
                    value_t r1,
                    value_t r2,
                    const VectorXd_t &x1, 
                    const VectorXd_t &x2, 
                    const VectorXd_t &x3,
                    const VectorXd_t &x4, 
                    const VectorXd_t &x5);

    //7变量交叉: x = x1 + r1 * rand() * (x3 - x2) + r2 * rand() * (x5 - x4) + r3 * rand() * (x7 - x6)
    void add7Rand(  VectorXd_t &x,
                    value_t r1,
                    value_t r2,
                    value_t r3,
                    const VectorXd_t &x1, 
                    const VectorXd_t &x2, 
                    const VectorXd_t &x3,
                    const VectorXd_t &x4, 
                    const VectorXd_t &x5,
                    const VectorXd_t &x6, 
                    const VectorXd_t &x7);

    //初始化
    bool initSolver();

    //更新最优变量
    void updateBestVar();

    //变量归一化
    void normVar(VectorXd_t &x, const VectorXd_t &x0);

    //获取[0,1]随机正态分布值
    value_t getRandNorm01();

    //获取[0,1]随机均匀分布值
    value_t getRandUniform01();
    
    //获取[0, num)之间随机整数
    std::size_t getRandIndex(std::size_t num);
    
    //数据复制
    void copyData(const std::shared_ptr<DataValue_t> &src, const std::shared_ptr<DataValue_t> &dst) const;

    //随机产生[startIndex, endIndex)整数
    std::size_t getRandInt(std::size_t startIndex, std::size_t endIndex);

    //返回当前变量均值
    void calcAvg(VectorXd_t &avg) const;

    //重置变量
    inline void resetVar(std::shared_ptr<Variable_t> &var);

protected:
    uint32_t id_;                             //求解器ID
    uint32_t printNum_;                       //每迭代几次打印一次结果,为0时表示不打印
    uint32_t costTime_;                       //求解耗时(s)
    SolverType_E type_;                       //求解器类型
    std::string name_;                        //求解器名称
    std::string resFile_;                     //求解结果保存到的文件
    std::shared_ptr<BaseFun> fun_;            //优化函数
    std::shared_ptr<Variable_t> bestVar_;     //最优变量
    std::shared_ptr<ResetParam_t> resetParam_;//重置初值条件

    std::random_device rd_;
    std::mt19937      gen_;
    std::normal_distribution<value_t> randNorm01_;//大致在[0,1]区间的正态分布
    std::uniform_real_distribution<value_t> randUniform01_;//[0,1]均匀分布
    
    Condition_t condition_;//迭代限制条件
    std::chrono::_V2::system_clock::time_point startTime_;//优化起始时间戳

    VectorXd_t vecMin_;//变量下限
    VectorXd_t vecMax_;//变量上限
    VectorXd_t vecDiff_;//梯度变量临时值
    vecValue_t vecHistoryY_;//记录历史迭代最优值

    std::vector<std::size_t> vecVarIndex_;//变量对应索引乱序
    std::vector<std::shared_ptr<Variable_t>> vecVariable_;//所有变量
};

using vecBaseSolver_t = std::vector<std::shared_ptr<BaseSolver>>;
using mapSolver_t     =std::map<SolverType_E, std::function<std::shared_ptr<BaseSolver>()>>;
}